#include "PsoParticle.h"

#include <sstream>

namespace jcrada {

    PsoParticle::PsoParticle( int size ) :
    _size( size ),
    _velocity( new double[size] ),
    _max_velocity( new double[size] ),
    _position( new double[size] ),
    _min_position( new double[size] ),
    _max_position( new double[size] ),
    _space( new eSpace[size] ),
    _pbest( new double[size] ),
    _nbest( new double[size] ),
    _inertia( 1.0 ),
    _fitness( 0.0 ),
    _fitness_nbest( 0.0 ),
    _fitness_pbest( 0.0 ) {
        for (int i = 0; i < getSize(); ++i) {
            setVelocity(i, 0.0);
            setMaxVelocity(i, 100.0);
            setPosition(i, 0.0);
            setMinPosition(i, 0.0);
            setMaxPosition(i, 100.0);
            setSpace(i, S_CONTINUOUS);
            setBestPersonalPosition(i, 0.0);
            setBestNeighborhoodPosition(i, 0.0);
        }
    }

    PsoParticle::~PsoParticle( ) {
        delete [] _velocity;
        delete [] _position;
        delete [] _space;
        delete [] _pbest;
        delete [] _nbest;
    }

    PsoParticle* PsoParticle::clone( ) const {
        PsoParticle* result = new PsoParticle(getSize());
        result->setFitness(getFitness());
        result->setFitnessForNeighborhoodBest(getFitnessForNeighborhoodBest());
        result->setFitnessForPersonalBest(getFitnessForPersonalBest());
        result->setInertia(getInertia());
        for (int i = 0; i < getSize(); ++i) {
            result->setVelocity(i, getVelocity(i));
            result->setMaxVelocity(i, getMaxVelocity(i));
            result->setPosition(i, getPosition(i));
            result->setMinPosition(i, getMinPosition(i));
            result->setMaxPosition(i, getMaxPosition(i));
            result->setSpace(i, getSpace(i));
            result->setBestPersonalPosition(i, getBestPersonalPosition(i));
            result->setBestNeighborhoodPosition(i, getBestNeighborhoodPosition(i));
        }
        return result;
    }

    std::string PsoParticle::toString( ) const {
        std::stringstream result;
        result << "Particle[fitness=" << getFitness() << ";";
        result << "Pos=" << toString(getPosition()) << ";";
        result << "Vel=" << toString(getVelocity()) << ";";
        result << "PBest=" << toString(getBestPersonalPosition()) << ";";
        result << "NBest=" << toString(getBestNeighborhoodPosition()) << "]";
        return result.str();
    }

    std::string PsoParticle::toString( const double * component ) const {
        std::stringstream result;
        result << "( ";
        for (int i = 0; i < getSize(); i++) {
            result << component[i];
            if (i + 1 < getSize()) {
                result << " , ";
            }
        }
        result << " )";
        return result.str();
    }

    void PsoParticle::setMaxVelocity( double value ) {
        for (int i = 0; i < getSize(); ++i) {
            setMaxVelocity(i, value);
        }
    }

    void PsoParticle::setMaxPosition( double value ) {
        for (int i = 0; i < getSize(); ++i) {
            setMaxPosition(i, value);
        }
    }

    void PsoParticle::setMinPosition( double value ) {
        for (int i = 0; i < getSize(); ++i) {
            setMinPosition(i, value);
        }
    }

    void PsoParticle::setSpace( eSpace space ) {
        for (int i = 0; i < getSize(); ++i) {
            setSpace(i, space);
        }
    }
    ///////////////
    //XML
    ///////////////

    PsoParticle* PsoParticle::fromXml( const QDomElement& xml, bool* ok ) {
        int size = xml.attribute("size", "0").toInt(ok);
        PsoParticle* result = new PsoParticle(size);
        result->setInertia(xml.attribute("inertia", "1.0").toDouble(ok));
        result->setFitness(xml.attribute("fitness", "0").toDouble(ok));
        result->setFitnessForNeighborhoodBest(xml.attribute("fitness-nbest", "0").toDouble(ok));
        result->setFitnessForPersonalBest(xml.attribute("fitness-pbest", "0").toDouble(ok));

        QDomNode child = xml.firstChild();
        //TODO: Peligroso porque el orden es IMPoRTANTE!
        int i = 0;
        while (!child.isNull()) {
            assert(i < size);
            QDomElement e = child.toElement();
            if (e.tagName() == "velocity") {
                result->setVelocity(i++, e.attribute("value", "0.0").toDouble(ok));
            } else if (e.tagName() == "max_velocity") {
                result->setMaxVelocity(i, e.attribute("value", "0.0").toDouble(ok));
            } else if (e.tagName() == "position") {
                result->setPosition(i, e.attribute("value", "0.0").toDouble(ok));
            } else if (e.tagName() == "min-position") {
                result->setMinPosition(i, e.attribute("value", "0.0").toDouble(ok));
            } else if (e.tagName() == "max-position") {
                result->setMaxPosition(i, e.attribute("value", "0.0").toDouble(ok));
            } else if (e.tagName() == "space") {
                result->setSpace(i,(eSpace) e.attribute("value", "0").toInt(ok));
            } else if (e.tagName() == "pbest") {
                result->setBestPersonalPosition(i, e.attribute("value", "0.0").toDouble(ok));
            } else if (e.tagName() == "nbest") {
                result->setBestNeighborhoodPosition(i, e.attribute("value", "0.0").toDouble(ok));
                ++i;
            }
            child = child.nextSibling();
        }
        return result;
    }

    void PsoParticle::toXml( QDomDocument& xml, QDomElement* parent) const {
        QDomElement particle = xml.createElement("PsoParticle");
        particle.setAttribute("size", getSize());
        particle.setAttribute("inertia", getInertia());
        particle.setAttribute("fitness", getFitness());
        particle.setAttribute("fitness-nbest", getFitnessForNeighborhoodBest());
        particle.setAttribute("fitness-pbest", getFitnessForPersonalBest());

        for (int i = 0; i < getSize(); ++i) {
//            QDomElement velocity = xml.createElement("velocity");
//            QDomElement max_velocity = xml.createElement("max-velocity");
            QDomElement position = xml.createElement("position");
//            QDomElement min_position = xml.createElement("min-position");
//            QDomElement max_position = xml.createElement("max-position");
//            QDomElement space = xml.createElement("space");
            QDomElement pbest = xml.createElement("pbest");
            QDomElement nbest = xml.createElement("nbest");

//            velocity.setAttribute("value", getVelocity(i));
//            max_velocity.setAttribute("value", getMaxVelocity(i));
            position.setAttribute("value", getPosition(i));
//            min_position.setAttribute("value", getMinPosition(i));
//            max_position.setAttribute("value", getMaxPosition(i));
//            space.setAttribute("value", getSpace(i));
            pbest.setAttribute("value", getBestPersonalPosition(i));
            nbest.setAttribute("value", getBestNeighborhoodPosition(i));

//            particle.appendChild(velocity);
//            particle.appendChild(max_velocity);
            particle.appendChild(position);
//            particle.appendChild(min_position);
//            particle.appendChild(max_position);
//            particle.appendChild(space);
            particle.appendChild(pbest);
            particle.appendChild(nbest);
        }
        if (parent) {
            parent->appendChild(particle);
        } else {
            xml.appendChild(particle);
        }
    }

}
